#include <linux/sched.h>
#include <linux/kernel.h>
#include <asm/system.h>

#include "blk.h"

struct request request[NR_REQUEST];

struct task_struct *wait_for_request = NULL;    // 请求数组没有空闲项时的临时等待处

struct blk_dev_struct blk_dev[NR_BLK_DEV] = {
    { NULL, NULL },     // 0 - 无设备
    { NULL, NULL },     // 1 - 内存
    { NULL, NULL },     // 2 - 软驱设备
    { NULL, NULL },     // 3 - 硬盘设备
    { NULL, NULL },     // 4 - ttyx设备
    { NULL, NULL },     // 5 - tty设备
    { NULL, NULL }      // 6 - lp打印机设备
};

static void add_request(struct blk_dev_struct *dev, struct request *req)
{
    struct request *tmp;

    req->next = NULL;
    cli();

    if (!dev->current_request) {    // 如果设备没有读写请求
        dev->current_request = req;
        sti();
        (dev->request_fn)();
        return;
    }
    
    // 将请求加入链表中
    for (tmp = dev->current_request; tmp->next; tmp = tmp->next)
        if ((IN_ORDER(tmp, req) || 
            !IN_ORDER(tmp, tmp->next)) &&
            IN_ORDER(req, tmp->next))
            break;
    req->next = tmp->next;
    tmp->next = req;
    sti();
}

static void make_request(int major, int rw, struct buffer_head *bh)
{
    struct request *req;

    if (rw != READ && rw != WRITE)
        panic("Bad block dev command, must be R/W/RA/WA");

repeat:
    if (rw == READ)
        req = request + NR_REQUEST; // 对于读请求，将指针指向队列尾部
    else
        req = request + ((NR_REQUEST * 2) / 3); // 对于写请求，指针指向队列2/3处

    while (--req >= request)    // 搜索一个空请求项
        if (req->dev < 0)
            break;
    
    if (req < request) {        // 如果没有找到空闲项，则让该次请求操作睡眠
        sleep_on(&wait_for_request);
        goto repeat;
    }

    req->dev = bh->b_dev;
    req->cmd = rw;
    req->errors = 0;
    req->sector = bh->b_blocknr << 1;
    req->nr_sectors = 2;
    req->buffer = bh->b_data;
    req->bh = bh;
    req->next = NULL;
    add_request(major + blk_dev, req);
}

void ll_rw_block(int rw, struct buffer_head *bh)
{
    unsigned int major = MAJOR(bh->b_dev);

    if (major >= NR_BLK_DEV || !(blk_dev[major].request_fn)) {
        printk("Trying to read nonexistent block-device\n\r");
        return;
    }
    make_request(major, rw, bh);
}

void blk_dev_init(void)
{
    int i;

    for (i = 0; i < NR_REQUEST; i++) {
        request[i].dev = -1;
        request[i].next = NULL;
    }
}
